package com.lins.store.aop;

import com.lins.store.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;

@Slf4j
@Aspect //标记为切面类
@Component("as")
public class TimerAspect {

    @Autowired
    HttpSession session;

    @Autowired
    UserMapper userMapper;

    /**
     * service类所有方法被AOP监控
     *
     * 使用around环绕通知的方法：
     * 【https://zhuanlan.zhihu.com/p/496039764】
     *
     * @param pjp ProceedingJoinPoint接口表示连接点，指向目标方法的对象
     * @return
     * @throws Throwable
     * execution(方法返回值 包.类.方法(参数))
     */
    //@Around("execution(* com.lins.store.controller.*.*(..))")  //包括注册也会被拦截
//    @Around("execution(* com.lins.store.controller.FileController.*(..))")
    @Around(value = "execution(* com.lins.store.bo.FileService.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        //获取目标方法所有的参数
        Object[] args = pjp.getArgs();
        for (Object arg : args){
//            System.out.println(arg.toString());
        }

        long start = System.currentTimeMillis();
        // 执行连接点方法，即切面所在位置对应的方法。调用目标方法，比如调用了login这个方法
        Object result = pjp.proceed();//执行
        // 每个方法执行前检测用户状态是否正常            //Interceptor拦截器可以解决
        /*Object uid = session.getAttribute("uid");
        if (uid == null) throw new UserNotFoundException("session is null");
        Integer uidValue = Integer.valueOf(uid.toString());
        UserEntity userEntity = userMapper.findByUid(uidValue);
        if (userEntity == null) throw new UserNotFoundException("用户状态异常！");*/
        long end = System.currentTimeMillis();
        //日志配置中，如未包含本包路径，则不能打印。
        log.trace("耗时：{}ms.",(end - start));
        // 返回连接点方法的返回值
        return result;
        /**
         * 这里我是想，在调用file接口方法前先判断该用户是否还存在，不存在则抛出异常。
         * 但是在测试时，因为登录admin后，session被覆盖，导致再拿到的session其实是admin的，故该校验永远不生效。
         * 并且，已经被删除的用户的操作，会被认定为是admin的操作。
         * 多用户登录导致session相互覆盖，是否可以把session对象放入List来统一管理。
         * 相关参考：【https://blog.csdn.net/qq_38322989/article/details/125000746】
         * 方案1：在登录时加入非空判断，若同浏览器有人已经登录，则提示异常。
         * 方案2：每个请求发来时，都需要从前端携带自己的uid，以此来区分用户。
         * 方案3：使用spring security
         */
    }

    /*可自定义切点位置，针对不同切点，方法上的@Around()可以这样写ex：@Around(value = "methodPointcut() && args(..)")
    @Pointcut(value = "@annotation(com.rq.aop.common.annotation.MyAnnotation)")
    public void methodPointcut(){}

    @Pointcut(value = "@annotation(com.rq.aop.common.annotation.MyAnnotation2)")
    public void methodPointcut2(){}
    */

    //定义增强，pointcut连接点使用@annotation(xxx)进行定义，传入注解参数
    @Around(value = "@annotation(around)") //around 与 下面参数名around对应
    public void processAuthority(ProceedingJoinPoint point,MyAnnotation around) throws Throwable{
        System.out.println("ANNOTATION welcome");
        System.out.println("ANNOTATION 调用方法："+ around.methodName());
        System.out.println("ANNOTATION 调用类：" + point.getSignature().getDeclaringTypeName());
        System.out.println("ANNOTATION 调用类名" + point.getSignature().getDeclaringType().getSimpleName());
        point.proceed(); //切入点调用目标方法
        System.out.println("ANNOTATION login success");
    }

    //

}
